package org.gson.util;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.internal.$Gson$Types;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 别人
 */
public class GsonUtils {

    private static Gson gson = null;

    static {
        gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
    }

    /**
     * Object 转成 json
     */
    public static String toJson(Object src) {
        return gson.toJson(src);
    }

    /**
     * json 转成 对应 cls 的 Object
     */
    public static <T> T fromJson(String json, Class<T> cls) {
        return gson.fromJson(json, cls);
    }

    /**
     * json 转成指定的 cls 的 List
     *
     * @param classOfT 借助传入 Class 的泛型来进行返回
     * @deprecated 该方法属于自欺欺人的方式来使用泛型。
     * 因为T在类型擦拭之后实际上就是Object，且fromJson底层返回类型为com.google.gson.internal.LinkedTreeMap
     * 那么只要触发强转的时候就会抛出异常。
     */
    @Deprecated
    public static <T> List<T> fromJsonList(String json, Class<T> classOfT) {
        return gson.fromJson(
                json,
                new TypeToken<List<T>>() {
                }.getType()
        );
    }

    /**
     * json 转成指定的 rawClass<typeParameters>，专用于处理泛型类型的json解析
     *
     * @param rawClass       泛型类型的原始类型(请尽量不要使用接口，最好指定实现类)
     * @param typeParameters 泛型类型的类型参数列表
     * @see $Gson$Types#canonicalize(Type) 实现原理
     */
    public static <T> T fromJsonGeneric(String json, Class<T> rawClass, Class... typeParameters) {
        Type type = new ParameterizedTypeForReturnType(rawClass, typeParameters);
        return gson.fromJson(json, type);
    }

    /**
     * 返回类型的参数化类型定义
     */
    private static class ParameterizedTypeForReturnType implements ParameterizedType {
        private final Class raw;
        private final Type[] args;

        public ParameterizedTypeForReturnType(Class raw, Type[] args) {
            this.raw = raw;
            this.args = args.length > 0 ? args : this.initArgs(raw);
        }

        private Type[] initArgs(Class raw) {
            Type[] types = new Class[raw.getTypeParameters().length];
            for (int i = 0; i < types.length; i++) {
                types[i] = Object.class;
            }
            return types;
        }

        @Override
        public Type[] getActualTypeArguments() {
            return args;
        }

        @Override
        public Type getRawType() {
            return raw;
        }

        @Override
        public Type getOwnerType() {
            return null;
        }
    }

    public static void main(String[] args) {
        Map map = GsonUtils.fromJsonGeneric(toJson(Collections.singletonMap("grs", 57)), HashMap.class, String.class, Integer.class);
        System.out.println(map);
        System.out.println(map.getClass());
    }

}
